﻿using AuSys.Database.Context;
using AuSys.Models;

namespace AuSys.Repos
{
    public abstract class RepoTree<T, TCache>(
        DataContext context
        ) : RepoCache<T, TCache>(context) 
        where T : class, IDbModel, ITreeNode
        where TCache : class, ITreeNode
    {
        protected IQueryable<T> FilterChildrenOf(int id)
            => Existing.Where(x => x.ParentId == id);
        protected IQueryable<T> FilterRoots()
            => FilterChildrenOf(0);

        protected bool HaveAncestorRelation(int ancestor, int descendant)
        {
            var cursor = CacheSearchSingle(x => x.Id == descendant)
                ?? throw new InvalidOperationException(
                    $"数据异常（找不到{nameof(T)}{descendant}）");
            var safety = CacheCount();
            while (safety > 0)
            {
                if (cursor.ParentId == ancestor)
                    return true;
                if (cursor.ParentId == 0)
                    return false;
                var cursorParent = CacheSearchSingle(x => x.Id == cursor.ParentId)
                    ?? throw new InvalidOperationException(
                        $"数据异常（找不到{nameof(T)}{cursor.ParentId}）");
                cursor = cursorParent;
                safety--;
            }
            throw new InvalidOperationException(
                $"数据异常（{nameof(T)}循环）");
        }

        protected List<int> GetDescendantIdsOf(int id)
        {
            var res = new List<int>();
            Queue<int> watches = new([id]);
            var safety = CacheCount();
            while (watches.Count > 0)
            {
                var watch = watches.Dequeue();
                var children = CacheSearchRange(x => x.ParentId == watch);
                foreach (var child in children)
                {
                    res.Add(child.Id);
                    if(res.Count > safety)
                        throw new InvalidOperationException(
                            $"数据异常（{nameof(T)}循环）");
                    watches.Enqueue(child.Id);
                }
            }
            return res;
        }
        private class TreeNode(int id, int parentId) : ITreeNode
        {
            public int Id { get; set; } = id;
            public int ParentId { get; set; } = parentId;
        }
    }
}
